package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.ApArticleConfigMapper;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.ApArticleService;
import com.heima.article.service.ArticleFreemarkerService;
import com.heima.common.constants.ArticleBehaviorConstants;
import com.heima.common.constants.ArticleConstants;
import com.heima.common.exception.CustomException;
import com.heima.common.redis.CacheService;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.dtos.ArticleInfoDto;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.pojos.ApArticleConfig;
import com.heima.model.article.pojos.ApArticleContent;
import com.heima.model.article.vos.ArticleCommentVo;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.comment.dtos.CommentManageDto;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.ArticleVisitStreamMess;
import com.heima.model.user.pojos.ApUser;
import com.heima.model.wemedia.dtos.StatisticsDto;
import com.heima.model.wemedia.vos.NewsDimensionVo;
import com.heima.utils.thread.AppThreadLocalUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * ClassName: ApArticleServiceImpl
 * Description:
 * date: 2023/8/5 0005 17:56
 *
 * @author zHan
 */
@Service
@Slf4j
@Transactional
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {
    public static final Integer DEFAULT_PAGE_SIZE = 10;   // 默认页数
    public static final Integer MAX_PAGE_SIZE = 50;     // 最大页数

    @Autowired
    private ApArticleMapper apArticleMapper;

    @Autowired
    private ApArticleConfigMapper apArticleConfigMapper;

    @Autowired
    private ApArticleContentMapper apArticleContentMapper;

    @Autowired
    private ArticleFreemarkerService articleFreemarkerService;

    /**
     * 加载页面
     *
     * @param articleHomeDto
     * @param type           1:加载更多 2：加载最新
     * @return
     */
    @Override
    public ResponseResult load(ArticleHomeDto articleHomeDto, Short type) {
        // 参数校验
        // 分页条数校验
        Integer size = articleHomeDto.getSize();
        if (size == null || size == 0) {
            // 设置默认页数为10
            size = DEFAULT_PAGE_SIZE;
        }
        // 页数不超过50
        size = Math.min(size, MAX_PAGE_SIZE);
        articleHomeDto.setSize(size);
        // 时间校验
        if (articleHomeDto.getMaxBehotTime() == null) {
            articleHomeDto.setMaxBehotTime(new Date());
        }
        if (articleHomeDto.getMinBehotTime() == null) {
            articleHomeDto.setMinBehotTime(new Date());
        }
        // 频道ID校验
        if (StringUtils.isBlank(articleHomeDto.getTag())) {
            articleHomeDto.setTag(ArticleConstants.DEFAULT_TAG);
        }
        // type的校验
        if (!type.equals(ArticleConstants.LOADTYPE_LOAD_MORE) && !type.equals(ArticleConstants.LOADTYPE_LOAD_NEW)) {
            type = ArticleConstants.LOADTYPE_LOAD_MORE;
        }
        // 查询
        List<ApArticle> apArticleList = apArticleMapper.loadArticleList(articleHomeDto, type);
        return ResponseResult.okResult(apArticleList);
    }

    /**
     * 保存app端相关文章
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult saveArticle(ArticleDto dto) {
        // 检查参数
        if (dto == null) {
            throw new CustomException(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        // 创建对象并拷贝
        ApArticle apArticle = new ApArticle();
        BeanUtils.copyProperties(dto, apArticle);
        // 判断id是否存在
        if (dto.getId() == null) {
            // 不存在，新增：文章表、文章配置表、文章内容表
            // 保存文章
            save(apArticle);
            // 保存文章配置
            ApArticleConfig apArticleConfig = new ApArticleConfig(apArticle.getId());
            apArticleConfigMapper.insert(apArticleConfig);
            // 保存文章内容
            ApArticleContent apArticleContent = new ApArticleContent();
            apArticleContent.setArticleId(apArticle.getId());
            apArticleContent.setContent(dto.getContent());
            apArticleContentMapper.insert(apArticleContent);
        } else {
            // 存在，修改：文章表、文章内容表
            // 修改文章
            updateById(apArticle);
            // 修改文章内容
            ApArticleContent apArticleContent =
                    apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery()
                            .eq(ApArticleContent::getArticleId, apArticle.getId()));
            apArticleContent.setContent(dto.getContent());
            apArticleContentMapper.updateById(apArticleContent);
        }

        // 异步调用，将静态文件上传到minio
        articleFreemarkerService.buildArticleDetailToMinio(apArticle, dto.getContent());

        // 返回文章id
        return ResponseResult.okResult(apArticle.getId());
    }

    @Autowired
    private CacheService cacheService;

    /**
     * 加载文章行为
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult loadArticleBehavior(ArticleInfoDto dto) {
        // 检查参数
        if (dto.getArticleId() == null || dto.getAuthorId() == null) {
            throw new CustomException(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        // 获取当前用户
        ApUser user = AppThreadLocalUtil.getUser();
        if (user == null) {
            throw new CustomException(AppHttpCodeEnum.NEED_LOGIN);
        }
        // 定义key
        String likesKey = ArticleBehaviorConstants.LIKE + user.getId()
                + ":" + ArticleBehaviorConstants.ARTICLE_TYPE + ":" + dto.getArticleId();
        String unlikeKey = ArticleBehaviorConstants.UNLIKE + user.getId()
                + ":" + dto.getArticleId();
        String collectionKey = ArticleBehaviorConstants.COLLECTION + user.getId()
                + ":" + 0 + ":" + dto.getArticleId();
        String followKey = ArticleBehaviorConstants.FOLLOW + user.getId()
                + ":" + dto.getAuthorId();
        // 得到各自的redis的key
        String like = cacheService.get(likesKey);
        String unlike = cacheService.get(unlikeKey);
        String collection =
                cacheService.get(collectionKey);
        String follow = cacheService.get(followKey);
        // 封装map集合
        Map<String, Boolean> map = new HashMap<>();
        // 得到各自的结果
        addMap(like, "islike", map);
        addMap(unlike, "isunlike", map);
        addMap(collection, "iscollection", map);
        addMap(follow, "isfollow", map);
        // 返回结果
        return ResponseResult.okResult(map);
    }

    /**
     * 添加结果map集合
     *
     * @param redisValue
     * @param mapKey
     * @param map
     */
    private void addMap(String redisValue, String mapKey, Map<String, Boolean> map) {
        Map redisMap = JSON.parseObject(redisValue, Map.class);
        if (redisMap == null) {
            map.put(mapKey, false);
            return;
        }
        Integer operation = (Integer) redisMap.get("operation");
        if (operation != null && operation == 0) {
            map.put(mapKey, true);
        }
    }

    /**
     * 加载首页
     *
     * @param dto
     * @param type     1:加载更多 2：加载最新
     * @param homePage true：是首页 false 不是首页
     * @return
     */
    @Override
    public ResponseResult loadHomePage(ArticleHomeDto dto, Short type, boolean homePage) {
        // 是首页
        if (homePage) {
            // 从缓存中获取数据
            String dataJson =
                    cacheService.get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + dto.getTag());
            if (StringUtils.isNotBlank(dataJson)) {
                List<HotArticleVo> hotArticleVoList = JSON.parseArray(dataJson, HotArticleVo.class);
                return ResponseResult.okResult(hotArticleVoList);
            }
        }
        // 不是首页
        return load(dto, type);
    }

    /**
     * 更新文章分值，同时更新redis缓存
     *
     * @param mess
     */
    @Override
    public void updateScore(ArticleVisitStreamMess mess) {
        // 更新文章的阅读、点赞、收藏、评论数量
        ApArticle apArticle = updateArticle(mess);
        // 计算文章分值
        Integer score = getScore(apArticle);
        score *= 3;
        // 替换当前文章对应的热点数据
        replaceHotArticleToRedis(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId(), apArticle, score);
        // 替换推荐的热点数量
        replaceHotArticleToRedis(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG, apArticle, score);

    }

    /**
     * 替换文章对应的热点数据，并存入redis
     *
     * @param HOT_ARTICLE_FIRST_PAGE
     * @param apArticle
     * @param score
     */
    private void replaceHotArticleToRedis(String HOT_ARTICLE_FIRST_PAGE, ApArticle apArticle,
                                          Integer score) {
        String ArticleAllJson =
                cacheService.get(HOT_ARTICLE_FIRST_PAGE);
        if (StringUtils.isNotBlank(ArticleAllJson)) {
            List<HotArticleVo> hotArticleVoList = JSON.parseArray(ArticleAllJson,
                    HotArticleVo.class);
            boolean flag = true;
            // 如果缓存中存在该数据，替换
            for (HotArticleVo hotArticleVo : hotArticleVoList) {
                if (hotArticleVo.getId().equals(apArticle.getId())) {
                    hotArticleVo.setScore(score);
                    flag = false;
                    break;
                }
            }
            // 如果不存在，与分值最小的数据比较分值，大于则替换替换
            if (flag) {
                if (hotArticleVoList.size() > 30) {
                    hotArticleVoList = hotArticleVoList.stream()
                            .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                            .collect(Collectors.toList());
                    HotArticleVo hotArticleVo = hotArticleVoList.get(hotArticleVoList.size() - 1);
                    if (score > hotArticleVo.getScore()) {
                        // 替换
                        hotArticleVoList.remove(hotArticleVo);
                        // 创建对象
                        HotArticleVo hot = new HotArticleVo();
                        BeanUtils.copyProperties(apArticle, hot);
                        hot.setScore(score);
                        hotArticleVoList.add(hot);
                    }
                } else {
                    // 数量不到30，新增
                    HotArticleVo hot = new HotArticleVo();
                    BeanUtils.copyProperties(apArticle, hot);
                    hot.setScore(score);
                    hotArticleVoList.add(hot);
                }
            }
            // 缓存的redis
            hotArticleVoList = hotArticleVoList.stream()
                    .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                    .collect(Collectors.toList());
            cacheService.set(HOT_ARTICLE_FIRST_PAGE,
                    JSON.toJSONString(hotArticleVoList));
        }
    }

    /**
     * 更新文章的阅读、点赞、收藏、评论数量
     *
     * @param mess
     */
    private ApArticle updateArticle(ArticleVisitStreamMess mess) {
        ApArticle apArticle = getById(mess.getArticleId());
        apArticle.setCollection(apArticle.getCollection() == null ? mess.getCollect() :
                apArticle.getCollection() + mess.getCollect());
        apArticle.setLikes(apArticle.getLikes() == null ? mess.getLike() :
                apArticle.getLikes() + mess.getLike());
        apArticle.setComment(apArticle.getComment() == null ? mess.getComment() :
                apArticle.getComment() + mess.getComment());
        apArticle.setViews(apArticle.getViews() == null ? mess.getView() :
                apArticle.getViews() + mess.getView());
        updateById(apArticle);
        return apArticle;
    }

    /**
     * 计算分值
     *
     * @param apArticle
     * @return
     */
    private Integer getScore(ApArticle apArticle) {
        Integer score = 0;
        if (apArticle.getViews() != null) {
            score += apArticle.getViews();
        }
        if (apArticle.getLikes() != null) {
            score += apArticle.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        if (apArticle.getComment() != null) {
            score += apArticle.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        if (apArticle.getCollection() != null) {
            score += apArticle.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        return score;
    }

    /**
     * 查询评论列表
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult findNewsComments(CommentManageDto dto) {
        // 检查参数
        dto.checkParam();
        if (dto.getBeginDate() == null || dto.getEndDate() == null) {
            dto.setBeginDate(new Date());
            dto.setEndDate(new Date());
        }
        // 查询
        List<ApArticle> apArticleList = list(Wrappers.<ApArticle>lambdaQuery()
                .eq(ApArticle::getAuthorId, dto.getAuthorId())
                .between(dto.getBeginDate() != null && dto.getEndDate() != null,
                        ApArticle::getCreatedTime, dto.getBeginDate(), dto.getEndDate())
                .orderByDesc(ApArticle::getPublishTime));
        // 循环遍历
        List<ArticleCommentVo> articleCommentVoList = apArticleList.stream().map(article -> {
                    // 创建vo对象
                    ArticleCommentVo articleCommentVo = new ArticleCommentVo();
                    // 对象拷贝
                    BeanUtils.copyProperties(article, articleCommentVo);
                    // 查询文章配置
                    Boolean isComment =
                            apArticleConfigMapper.selectOne(Wrappers.<ApArticleConfig>lambdaQuery()
                                    .eq(ApArticleConfig::getArticleId, article.getId())).getIsComment();
                    // 补全数据
                    articleCommentVo.setComments(article.getComment());
                    articleCommentVo.setIsComment(isComment);
                    return articleCommentVo;
                }).sorted(Comparator.comparing(ArticleCommentVo::getCreatedTime))
                .collect(Collectors.toList());
        // 返回结果
        ResponseResult responseResult = new PageResponseResult(dto.getPage(), dto.getSize(),
                articleCommentVoList.size());
        responseResult.setData(articleCommentVoList);
        return responseResult;
    }

    /**
     * 管理端统计数据
     *
     * @param map
     * @return
     */
    @Override
    public ResponseResult getNewsDimension(Map<String, String> map) {
        // 获取数据
        Integer wmUserId = JSON.parseObject(map.get("wmUserId"), Integer.class);
        Date beginDate = JSON.parseObject(map.get("beginDate"), Date.class);
        Date endDate = JSON.parseObject(map.get("endDate"), Date.class);
        // 查询图文数据
        List<ApArticle> apArticleList = list(Wrappers.<ApArticle>lambdaQuery()
                .eq(wmUserId!=null,ApArticle::getAuthorId, wmUserId)
                .between(beginDate != null && endDate != null, ApArticle::getPublishTime,
                        beginDate, endDate));
        // 获得点赞数量集合
        List<Integer> likesList =
                apArticleList.stream()
                        .map(ApArticle::getLikes)
                        .filter(Objects::nonNull)   // 删除null
                        .collect(Collectors.toList());
        // 获得收藏数量集合
        List<Integer> collectList =
                apArticleList.stream()
                        .map(ApArticle::getCollection)
                        .filter(Objects::nonNull)   // 删除null
                        .collect(Collectors.toList());
        // 数量聚合
        Integer likesNum = likesList.stream().reduce(Integer::sum).orElse(0);
        Integer collectNum = collectList.stream().reduce(Integer::sum).orElse(0);
        // 封装vo对象
        NewsDimensionVo newsDimensionVo = new NewsDimensionVo();
        newsDimensionVo.setPublishNum(apArticleList.size());
        newsDimensionVo.setLikesNum(likesNum);
        newsDimensionVo.setCollectNum(collectNum);
        // 返回结果
        return ResponseResult.okResult(newsDimensionVo);
    }

    /**
     * 查询统计内容
     *
     * @param map
     * @return
     */
    @Override
    public ResponseResult getNewPage(Map<String, String> map) {
        // 获取数据
        Integer wmUserId = JSON.parseObject(map.get("wmUserId"), Integer.class);
        Date beginDate = JSON.parseObject(map.get("beginDate"), Date.class);
        Date endDate = JSON.parseObject(map.get("endDate"), Date.class);
        StatisticsDto dto = JSON.parseObject(map.get("dto"), StatisticsDto.class);
        // 查询数据
        List<ApArticle> apArticleList = list(Wrappers.<ApArticle>lambdaQuery()
                .eq(wmUserId!=null,ApArticle::getAuthorId, wmUserId)
                .between(beginDate != null && endDate != null,ApArticle::getPublishTime, beginDate, endDate));
        // 封装结果
        ResponseResult responseResult = new PageResponseResult(dto.getPage(), dto.getSize(),
                apArticleList.size());
        responseResult.setData(apArticleList);
        // 返回
        return responseResult;

    }
}

